home *** CD-ROM | disk | FTP | other *** search
Wrap
/************************************************************************************************************ Direct3DRM and DirectSound3D interface designed for the Viewer Sample Application (c) 1996 Microsoft Corporation ************************************************************************************************************/ #include "rlds3d.h" #include "ds3dvi.h" // "Internal" include stuff, like some structures // For resource symbols #include "resource.h" #include "d3drmwin.h" #include <windows.h> #include <windowsx.h> #include <stdio.h> #include <string.h> #include <malloc.h> #include <math.h> #include <direct.h> #include <objbase.h> #include <initguid.h> #include "rodcone.h" #include "file.h" #include <mmsystem.h> #include "dsound.h" #include "dsutil3d.h" /* ** This is an interface between the viewer and the RL/DS3D APIs which provides the ** functionality required by the viewer in simplified form. ** ** Note: This is not an object-oriented API since there should only be need for one ** copy at a time and this way C++ to C conversions should be fairly easy ** ** DESCRIPTION: The user interfaces the 3D world and objects therein by selecting an item ** with the mouse (on the screen) and performing operations on that item. The user will ** also be able to perform operations on the camera and a few global operations applied ** to all objects. ** ** USAGE: Create the interface using RLDS3D_Initialize() (returns FALSE if not created) ** and remove it using RLDS3D_Deinitialize(). The Render() functionality draws the world ** on the screen. Other functionality as described. */ /* *********************************** GLOBALS ************************************* */ /* ** VIEWER LPDIRECT3DRMFRAME APPDATA STRUCTURE ** ** Pointers to this class are placed (as necessary) in the appdata fields for LPDIRECT3DRMFRAMEs. Any ** necessary data to be attached to frames should be added to this class */ class FRAMEAPPDATA { public: // Sound buffer interfaces (the standard one and the 3D interface) LPDIRECTSOUNDBUFFER Sound; LPDIRECTSOUND3DBUFFER i3DSound; // Whether or not this frame is currently in orbit (and thus contained in a temporary frame) BOOL bOrbiting; // Whether or not this frame is a bullet (and thus has a bullet callback and dialog) BOOL bBullet; // A window handle associated with the orbiting/bullet state which controls speed, etc. HWND hDlg; FRAMEAPPDATA() : Sound(NULL), i3DSound(NULL), bOrbiting(FALSE), bBullet(FALSE), hDlg(0) {}; ~FRAMEAPPDATA() { if (i3DSound) i3DSound->Release(); if (Sound) Sound->Release(); }; }; typedef FRAMEAPPDATA* LPFRAMEAPPDATA; // Interface to the D3DRM functions LPDIRECT3DRM lpD3DRM = NULL; // Pointer to the Directsound API LPDIRECTSOUND lpDS = NULL; // Clipper for the DDraw surface LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // Handle to the API's parent window (passed in Initialize) HWND hwndParent; // Handle to our instance HANDLE hinst; AppInfo* info; /* ** Globals used for editing what is in the D3DRM world */ // Selected frame (contains visual currently selected by user) LPDIRECT3DRMFRAME sFrame = NULL; // Whether or not editing box should be shown around the selected object BOOL showBoxes = FALSE; // Currently selected visual (if one is selected) static LPDIRECT3DRMMESHBUILDER sVisual = NULL; // Currently selected light (if one is selected) static LPDIRECT3DRMLIGHT sLight = NULL; // Box/Speaker cone to go around the selected item (if boxes are on and if there IS a speaker) static LPDIRECT3DRMMESH selectionBox = NULL; static LPDIRECT3DRMMESH selectionSpeaker = NULL; // Clipboard frame and visual allowing cutting and pasting LPDIRECT3DRMFRAME clipboardFrame = NULL; LPDIRECT3DRMVISUAL clipboardVisual = NULL; /* ** Globals for DirectSound3D */ // Listener information (connected to the camera) LPDIRECTSOUND3DLISTENER lp3DListenerInfo; // Primary Buffer (we don't REALLY need a pointer to it but we keep it just in case) LPDIRECTSOUNDBUFFER lpDSBuff; /* ************************ Internal function declarations **************************** */ // These are not part of the interface and are just used by the RLDS3D functions static BOOL CreateDevice(HWND win, AppInfo* info); HRESULT loadTextures(char *name, void *arg, LPDIRECT3DRMTEXTURE *tex); char* LSTRRCHR( const char* lpString, int bChar ); static void PlaceMesh(LPDIRECT3DRMMESHBUILDER mesh, AppInfo *info); static BOOL CreateScene(AppInfo* info); static BOOL RebuildDevice(HWND win, AppInfo* info, int width, int height); static LPDIRECT3DRMMESHBUILDER makeBox(D3DRMBOX*); void SelectVisual(LPDIRECT3DRMMESHBUILDER visual, LPDIRECT3DRMFRAME frame); int ChooseNewColor(HWND, D3DCOLOR*); void RemoveSoundRecord(LPDIRECT3DRMFRAME owner); void releaseSoundCallback(LPDIRECT3DRMFRAME frame); void StopOrbiting(LPDIRECT3DRMFRAME stopme); void UpdateConeVisual(void); /* *********************** Error checker functions ************************************ */ /* ** These are wrapped around DS or D3DRM calls to appropriately catch errors and handle them. ** ** The generalized cases are coded, callers can pass forced reactions to errors by using the force_critical variables */ /* ** In this code the general philosophy for when to use the viewer is when requesting anything from something outside the ** program. Thus, asking a frame which we know exists to accept an added visual doesn't get checked, but trying to create ** a new frame (which tries to allocate memory) is checked in case we've run out of free memory. */ BOOL D3DRM_SUCCEED(HRESULT result, int force_critical, char* info) { if (result == D3DRM_OK) return TRUE; // Could be 0 for non-critical, 1 for non-critical but reports it, or 2 for critical (notify and quit) int priority = 0; char* error_string; switch (result) { case D3DRMERR_BADALLOC: error_string = "Out of memory"; priority = 2; break; case D3DRMERR_BADDEVICE: error_string = "Device is not compatible with renderer"; priority = 2; break; case D3DRMERR_BADFILE: error_string = "Data file is corrupt"; priority = 2; break; case D3DRMERR_BADMAJORVERSION: error_string = "Bad DLL major version"; priority = 2; break; case D3DRMERR_BADMINORVERSION: error_string = "Bad DLL minor version"; priority = 2; break; case D3DRMERR_BADOBJECT: error_string = "Object expected in argument"; priority = 2; break; case D3DRMERR_BADTYPE: error_string = "Bad argument type passed"; priority = 1; break; case D3DRMERR_BADVALUE: error_string = "Bad argument value passed"; priority = 1; break; case D3DRMERR_FACEUSED: error_string = "Face already used in a mesh"; priority = 1; break; case D3DRMERR_FILENOTFOUND: error_string = "File cannot be opened"; priority = 1; break; case D3DRMERR_NOTDONEYET: error_string = "Unimplemented function called"; priority = 1; break; case D3DRMERR_NOTFOUND: error_string = "Object not found in specified place"; priority = 1; break; case D3DRMERR_UNABLETOEXECUTE: error_string = "Unable to carry out procedure"; priority = 2; break; default: error_string = "D3DRM Error: Unable to continue"; priority = 2; break; } int ret; if (force_critical >= 0) priority = force_critical; if (priority == 1) { ret = MessageBox(hwndParent, error_string, "D3DRM Warning", MB_APPLMODAL|MB_ICONWARNING|MB_OK); } else if (priority == 2) { ret = MessageBox(hwndParent, error_string, "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK); PostMessage(hwndParent, WM_CLOSE,0,0); } return FALSE; } /* ** DS isn't vital for the viewer to run so error messages don't force a quit */ BOOL DS3D_SUCCEED(HRESULT result, int force_critical, char* info) { if (result == DS_OK) return TRUE; // Could be 0 for no message, 1 for non-critical (simply needs to report it), or 2 for critical (notify and quit) int priority = 0; char* error_string = NULL; switch (result) { case DSERR_ALLOCATED: error_string = "Requested resources already in use"; priority = 1; break; case DSERR_ALREADYINITIALIZED: error_string = "Object already initialized"; priority = 0; break; case DSERR_BADFORMAT: error_string = "Wave format not supported"; priority = 0; break; case DSERR_BUFFERLOST: error_string = "Buffer lost and must be restored"; priority = 0; break; case DSERR_CONTROLUNAVAIL: error_string = "Control requested not available"; priority = 0; break; case DSERR_GENERIC: error_string = "Undetermined error"; priority = 1; break; case DSERR_INVALIDCALL: error_string = "Invalid call for object's current state"; priority = 0; break; case DSERR_INVALIDPARAM: error_string = "Invalid parameters passed to object"; priority = 0; break; case DSERR_NOAGGREGATION: error_string = "Object does not support aggregation"; priority = 1; break; case DSERR_NODRIVER: error_string = "No sound driver available"; priority = 1; break; // case DSERR_NOINTERFACE: error_string = "Requested COM interface not available"; priority = 0; break; case DSERR_OUTOFMEMORY: error_string = "Out of memory"; priority = 1; break; case DSERR_PRIOLEVELNEEDED: error_string = "Caller does not have required priority level"; priority = 0; break; // case DSERR_UNINITIALIZED: error_string = "DirectSound not initialized"; priority = 1; break; case DSERR_UNSUPPORTED: error_string = "Unsupported function called"; priority = 0; break; default: error_string = "Undetermined error"; priority = 1; break; } int ret; if (force_critical >= 0) priority = force_critical; if (priority == 1) { ret = MessageBox(hwndParent, error_string, "DS3D Warning", MB_APPLMODAL|MB_ICONWARNING|MB_OK); } else if (priority == 2) { ret = MessageBox(hwndParent, error_string, "DS3D Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK); PostMessage(hwndParent, WM_CLOSE,0,0); } return FALSE; } /* ************************ INITIALIZATION/DEINITIALIZATION **************************** */ /* ** Initialize will attach itself to the passed window. Call initialize after ** getting a handle for your window but before you display it. ** ** Initialize will initialize the D3DRM API and return false if it fails. It will ** also attempt to initialize a DirectSound3D API, but failing this does not ** justify a failed Initialize (since the 3D sound isn't a necessary part of ** the viewer) */ BOOL RLDS3D_Initialize(HWND hwndPW, HANDLE this_inst) { if (!hwndPW) return FALSE; hinst = this_inst; hwndParent = hwndPW; // D3DRM API INIT - we force non-critical warnings here because the initialization returns a boolean for success // Call Direct3DRMCreate to try to make a D3DRM object if (!D3DRM_SUCCEED(Direct3DRMCreate(&lpD3DRM), 1)) return FALSE; if (!D3DRM_SUCCEED(DirectDrawCreateClipper(0, &lpDDClipper, NULL),1)) { lpD3DRM->Release(); return FALSE; } if (!D3DRM_SUCCEED(lpDDClipper->SetHWnd(0, hwndParent),1)) { lpDDClipper->Release(); lpD3DRM->Release(); return FALSE; } // Set up D3DRM information structure info = (AppInfo*) malloc(sizeof(AppInfo)); if (!info) { lpDDClipper->Release(); lpD3DRM->Release(); return FALSE; } info->model = D3DCOLOR_MONO; // Calls our internal function to create a viewport and device and attach it to the window (CreateDevice in misc. functionality) // (also fills in the global information structure "info" for what D3DRM can do and is currently doing) if (CreateDevice(hwndParent, info) == FALSE) { lpDDClipper->Release(); lpD3DRM->Release(); return FALSE; } // DIRECTSOUND 3D INIT - note, if this fails the entire initialization is still OK. // Description for our primary buffer creation DSBUFFERDESC dsbd; int ret = IDRETRY; // Try to create the Directsound objects until we either do it, are told to ignore it, or are told to abort while (ret == IDRETRY) { // Create the directsound object if (DS3D_SUCCEED(DirectSoundCreate(NULL, &lpDS, NULL))) { // Set cooperative level if (DS3D_SUCCEED(lpDS->SetCooperativeLevel(hwndParent, DSSCL_PRIORITY))) { // Create a primary buffer so we can query for a 3D Listener interface memset(&dsbd, 0, sizeof(DSBUFFERDESC)); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D; if (DS3D_SUCCEED(lpDS->CreateSoundBuffer(&dsbd, &lpDSBuff, NULL))) { // Make the primary 44.1 KHz so that it sounds better WAVEFORMATEX wfx; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = 44100; wfx.nAvgBytesPerSec = 44100*2*2; wfx.nBlockAlign = 4; wfx.wBitsPerSample = 16; wfx.cbSize = 0; lpDSBuff->SetFormat(&wfx); // Get the 3D listener information (error currently ignored) if (DS3D_SUCCEED(lpDSBuff->QueryInterface(IID_IDirectSound3DListener, (void**) &lp3DListenerInfo))) { lp3DListenerInfo->SetDopplerFactor(D3DVAL(100.0), DS3D_IMMEDIATE); } else { // Failed to get listener info lpDSBuff->Release(); lpDS->Release(); lpDS = NULL; } } else { // Failed to create a primary buffer lpDS->Release(); lpDS = NULL; } } else { // Failed to set cooperative level lpDS->Release(); lpDS = NULL; } } // Warn that we could create the DirectSound object if (!lpDS) { ret = MessageBox(hwndParent, "DirectSound 3D could not initialize", "Warning", MB_APPLMODAL|MB_ICONWARNING|MB_ABORTRETRYIGNORE); if (ret == IDABORT) { lpDDClipper->Release(); lpD3DRM->Release(); return FALSE; } } else ret = IDOK; } return TRUE; } /* ** Deinitializes */ void RLDS3D_Deinitialize() { // Releases the DSound interface... this is very important! if (lpDS != NULL) { lpDSBuff->Release(); lpDS->Release(); } info->dev->Release(); lpDDClipper->Release(); lpD3DRM->Release(); free(info); } /* ******************************************** ADDING/REMOVING/EDITING OBJECTS ********************************* */ /* ** Loads XOF file into the RL world (with textures) */ void RLDS3D_LoadXOF(char* file) { if (!file) return; LPDIRECT3DRMMESHBUILDER builder = NULL; if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) { if (builder->Load(file, NULL, D3DRMLOAD_FROMFILE, loadTextures, NULL) != D3DRM_OK) { MessageBox(hwndParent, "Unable to load file", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONEXCLAMATION|MB_OK); builder->Release(); return; } PlaceMesh(builder, info); builder->Release(); } } /* ** Sets/Gets whether or not boxes are shown around selected item */ BOOL RLDS3D_GetBoxes(void) { return showBoxes; } void RLDS3D_SetBoxes(BOOL new_val) { showBoxes = new_val; // Re-selects the currently selected visual so that the box is generated/destroyed around it RLDS3D_UpdateSelectionBox(); } /* ** Updates the bounding box around the selected visual (this could be done using a render callback function to compare the ** frame's scaling and transform functions instead) */ void RLDS3D_UpdateSelectionBox(void) { // When we select this visual, we destroy any existing box and create a new one around it if showBoxes is true. SelectVisual(sVisual, sFrame); } /* ** Deselects the currently selected 3D visual. */ void RLDS3D_DeselectVisual() { // Removes the bounding box from around it if it's there if (sFrame && selectionBox) { sFrame->DeleteVisual(selectionBox); sFrame->DeleteVisual(selectionSpeaker); } sFrame = NULL; sLight = NULL; sVisual = NULL; selectionBox = NULL; selectionSpeaker = NULL; } /* ** Given coordinates it selects the first visual under those coordinates in the window's viewport */ void RLDS3D_FindAndSelectVisual(int x, int y, LPBOOL changed) { LPDIRECT3DRMVISUAL visual; LPDIRECT3DRMFRAME frame; LPDIRECT3DRMPICKEDARRAY picked; LPDIRECT3DRMFRAMEARRAY frames; LPDIRECT3DRMMESHBUILDER mesh; LPDIRECT3DRMVIEWPORT view = info->view; LPDIRECT3DRMFRAME oldframe = sFrame; /* * Make sure we don't try to select the selection box of the current * selection. */ RLDS3D_DeselectVisual(); view->Pick(x, y, &picked); if (picked) { if (picked->GetSize()) { // Get the top-level visual picked->GetPick(0, &visual, &frames, 0); // The frames that contain the visual are placed into a framearray in heiarchical order, take the // last one (the one most closely associated with the visual) frames->GetElement(frames->GetSize() - 1, &frame); // We can only select meshes so we query the visual to make sure it is one if (D3DRM_SUCCEED(visual->QueryInterface(IID_IDirect3DRMMeshBuilder, (void **) &mesh), 0)) { // If we're clicking on an orbiting frame then we need to stop it. StopOrbiting(frame); SelectVisual(mesh, frame); mesh->Release(); } frame->Release(); frames->Release(); visual->Release(); } picked->Release(); } if (changed) { if (sFrame == oldframe) *changed = FALSE; else *changed = TRUE; } } /* ** Cuts the current selection to the clipboard */ void RLDS3D_CutVisual() { LPDIRECT3DRMFRAME frame; if (clipboardFrame) clipboardFrame->Release(); if (sFrame) { clipboardFrame = sFrame; clipboardVisual = sVisual; // If a 3D sound is attached, remove it (sounds not carried to clipboard) RemoveSoundRecord(clipboardFrame); RLDS3D_DeselectVisual(); clipboardFrame->AddRef(); clipboardFrame->GetParent(&frame); if (frame) { frame->DeleteChild(clipboardFrame); frame->Release(); } } } /* ** Copies the current selection to the clipboard */ void RLDS3D_CopyVisual() { LPDIRECT3DRMFRAME frame; if (sFrame) { // Things could really foul up if the clones aren't created so we make sure they are if (!D3DRM_SUCCEED(sFrame->Clone(0, IID_IDirect3DRMFrame, (void **) &clipboardFrame))) { // If we've failed, we must make sure the clipboard is empty! clipboardVisual->Release(); clipboardVisual = NULL; return; } if (!D3DRM_SUCCEED(sVisual->Clone(0, IID_IDirect3DRMVisual, (void **) &clipboardVisual))) { // If we've failed, we must make sure the clipboard is empty! clipboardFrame->Release(); clipboardFrame = NULL; return; } // If a 3D sound is attached, remove it (sounds not carried to clipboard) RemoveSoundRecord(clipboardFrame); clipboardFrame->AddVisual(clipboardVisual); clipboardVisual->Release(); clipboardFrame->GetParent(&frame); if (frame) { frame->DeleteChild(clipboardFrame); frame->Release(); } } } /* ** Pastes the current selection to the window */ void RLDS3D_PasteVisual() { if (clipboardFrame) { LPDIRECT3DRMFRAME frame; LPDIRECT3DRMVISUAL visual; if (!D3DRM_SUCCEED(clipboardFrame->Clone(0, IID_IDirect3DRMFrame, (void **) &frame))) return; if (!D3DRM_SUCCEED(clipboardVisual->Clone(0, IID_IDirect3DRMVisual, (void **) &visual))) { frame->Release(); return; } frame->AddVisual(visual); info->scene->AddChild(frame); visual->Release(); frame->Release(); } } /* ** Deletes the current selection from the world without copying to the clipboard */ void RLDS3D_DeleteVisual() { if (sFrame) { LPDIRECT3DRMFRAME parent, frame; // Make a copy of the selected frame ('cause deselecting it will make it inaccessible) frame = sFrame; // If a 3D sound is attached, remove it RemoveSoundRecord(frame); // Deselect the frame (removes bounding box, etc, also sets sFrame to NULL) RLDS3D_DeselectVisual(); if (frame->GetAppData()) delete (FRAMEAPPDATA*)frame->GetAppData(); frame->GetParent(&parent); if (parent) { parent->DeleteChild(frame); parent->Release(); } } } /* ** Add a directional light */ void RLDS3D_AddDirectionalLight() { LPDIRECT3DRMMESHBUILDER builder; LPDIRECT3DRMLIGHT light; LPDIRECT3DRMFRAME frame; if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) { if (D3DRM_SUCCEED(builder->Load("camera.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) { builder->SetQuality(D3DRMRENDER_UNLITFLAT); if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) { if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) { frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0)); frame->AddVisual(builder); frame->AddLight(light); frame->Release(); } light->Release(); } } builder->Release(); } } /* ** Add a parallel point light */ void RLDS3D_AddParallelPointLight() { LPDIRECT3DRMMESHBUILDER builder; LPDIRECT3DRMLIGHT light; LPDIRECT3DRMFRAME frame; if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) { if (D3DRM_SUCCEED(builder->Load("sphere2.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) { builder->SetQuality(D3DRMRENDER_UNLITFLAT); builder->Scale(D3DVAL(0.2), D3DVAL(0.2), D3DVAL(0.2)); if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) { if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) { frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0)); frame->AddVisual(builder); frame->AddLight(light); frame->Release(); } light->Release(); } } builder->Release(); } } /* ** Add Point Light */ void RLDS3D_AddPointLight() { LPDIRECT3DRMMESHBUILDER builder; LPDIRECT3DRMLIGHT light; LPDIRECT3DRMFRAME frame; if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) { if (D3DRM_SUCCEED(builder->Load("sphere2.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) { builder->SetQuality(D3DRMRENDER_UNLITFLAT); builder->Scale(D3DVAL(0.2), D3DVAL(0.2), D3DVAL(0.2)); if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) { if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) { frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0)); frame->AddVisual(builder); frame->AddLight(light); frame->Release(); } light->Release(); } } builder->Release(); } } /* ** Add a spotlight */ void RLDS3D_AddSpotlight() { LPDIRECT3DRMMESHBUILDER builder; LPDIRECT3DRMLIGHT light; LPDIRECT3DRMFRAME frame; if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) { if (D3DRM_SUCCEED(builder->Load("camera.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) { builder->SetQuality(D3DRMRENDER_UNLITFLAT); if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_SPOT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) { if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) { frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0)); frame->AddVisual(builder); frame->AddLight(light); frame->Release(); } light->Release(); } } builder->Release(); } } /* *********************************** OBJECT MOTION/SCALING/COLORING **************************************** */ /* ** Chooses a new color and sets the selected object to it... handles the selection of the color, etc. */ void RLDS3D_SetSelColour(void) { if (!sFrame) return; LPDIRECT3DRMMESHBUILDER mesh; if (!D3DRM_SUCCEED(sVisual->QueryInterface(IID_IDirect3DRMMeshBuilder, (void**) &mesh),0)) return; if (sLight) { D3DCOLOR c = sLight->GetColor(); if (ChooseNewColor(hwndParent, &c)) { mesh->SetColor(c); sLight->SetColor(c); } } else { D3DCOLOR c; if (mesh->GetFaceCount()) { LPDIRECT3DRMFACEARRAY faces; LPDIRECT3DRMFACE face; mesh->GetFaces(&faces); faces->GetElement(0, &face); c = face->GetColor(); face->Release(); faces->Release(); } else c = D3DRMCreateColorRGB(D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)); if (ChooseNewColor(hwndParent, &c)) mesh->SetColor(c); } mesh->Release(); } /* ** Moves the camera relative to itself by providing scalars to multiply against the CAMERA-RELATIVE unit vectors ** forwards/up/right. */ void RLDS3D_SetCamVelRelToCam(D3DVALUE forward, D3DVALUE up, D3DVALUE right) { D3DVECTOR vDir, vUp, vRight; info->camera->GetOrientation(info->scene, &vDir, &vUp); // Cross the UP vector and the RIGHT vector to get the FORWARDS vector D3DRMVectorCrossProduct(&vRight, &vUp, &vDir); info->camera->SetVelocity(info->scene, vDir.x*forward + vUp.x*up + vRight.x*right, vDir.y*forward + vUp.y*up + vRight.y*right, vDir.z*forward + vUp.z*up + vRight.z*right, TRUE); } /* ** Rotates the camera around its three axis ** ** forward_axis is roll, up_axis is yaw, right_axis is pitch */ void RLDS3D_SetCamRotForward(D3DVALUE forward_axis) { info->camera->SetRotation(info->camera, 0.0f, 0.0f, 1.0f, forward_axis); } void RLDS3D_SetCamRotUp(D3DVALUE up_axis) { info->camera->SetRotation(info->camera, 0.0f, 1.0f, 0.0f, up_axis); } void RLDS3D_SetCamRotRight(D3DVALUE right_axis) { info->camera->SetRotation(info->camera, 1.0f, 0.0f, 0.0f, right_axis); } /* ** Scales the currently selected object by the scale factors... */ void RLDS3D_ScaleSelected(D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) { if (!sFrame) return; if (!sVisual) return; RLDS3D_StopOrbitSelected(); sVisual->Scale(sx, sy, sz); RLDS3D_UpdateSelectionBox(); } /* ** Moves the currently selected object in the 3D world relative to the camera */ void RLDS3D_SetSelectedVelRelToCam(D3DVALUE forward, D3DVALUE up, D3DVALUE right) { if (!sFrame) return; RLDS3D_StopOrbitSelected(); D3DVECTOR vDir, vUp, vRight; info->camera->GetOrientation(info->scene, &vDir, &vUp); D3DRMVectorCrossProduct(&vRight, &vUp, &vDir); sFrame->SetVelocity(info->scene, vDir.x*forward + vUp.x*up + vRight.x*right, vDir.y*forward + vUp.y*up + vRight.y*right, vDir.z*forward + vUp.z*up + vRight.z*right, TRUE); } /* ** Rotates the currently selected object relative to the camera's frame when passed ** a vector for the axis and an angle of rotation. ** This is useful because the AXIS which is specified is relative to what appears on the screen with it's origin at the camera ** (ie: (0,0,1) will be an axis straight into the screen and thus things will spin around the centre of the screen) */ void RLDS3D_SetSelectedRotRelToCam(D3DVALUE AxisX, D3DVALUE AxisY, D3DVALUE AxisZ, D3DVALUE angle) { if (!sFrame) return; RLDS3D_StopOrbitSelected(); sFrame->SetRotation(info->camera, AxisX, AxisY, AxisZ, angle); } /* ** Moves the currently selected object by x/y pixels on the screen from it's relative position. Allows a user ** to drag objects around the screen using the mouse. Assumes that the distance from the frame to the screen ** will remain constant */ void RLDS3D_MoveSelectedPosByScreenCoords(double delta_x, double delta_y) { if (!sFrame) return; RLDS3D_StopOrbitSelected(); D3DVECTOR p1; D3DRMVECTOR4D p2; sFrame->GetPosition(info->scene, &p1); info->view->Transform(&p2, &p1); // returned value is in homogenous coords so we need to multiply by w to get vector coords p2.x += D3DMultiply(D3DVAL((D3DVALUE)delta_x), p2.w); p2.y += D3DMultiply(D3DVAL((D3DVALUE)delta_y), p2.w); info->view->InverseTransform(&p1, &p2); sFrame->SetPosition(info->scene, p1.x, p1.y, p1.z); } void RLDS3D_GetDistanceFactor(D3DVALUE *temp) { lp3DListenerInfo->GetDistanceFactor(temp); } void RLDS3D_GetDopplerFactor(D3DVALUE *temp) { lp3DListenerInfo->GetDopplerFactor(temp); } void RLDS3D_GetRolloffFactor(D3DVALUE *temp) { lp3DListenerInfo->GetRolloffFactor(temp); } void RLDS3D_SetDistanceFactor(D3DVALUE temp) { lp3DListenerInfo->SetDistanceFactor(temp, DS3D_IMMEDIATE); } void RLDS3D_SetDopplerFactor(D3DVALUE temp) { lp3DListenerInfo->SetDopplerFactor(temp, DS3D_IMMEDIATE); } void RLDS3D_SetRolloffFactor(D3DVALUE temp) { lp3DListenerInfo->SetRolloffFactor(temp, DS3D_IMMEDIATE); } void RLDS3D_CommitDeferredSettings(void) { lp3DListenerInfo->CommitDeferredSettings(); } BOOL RLDS3D_SoundSelected(void) { if (!lpDS) return FALSE; if (!sFrame) return FALSE; if (!sFrame->GetAppData()) return FALSE; if (!((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound) return FALSE; return TRUE; } void RLDS3D_GetSelConeAngles(LPDWORD inner, LPDWORD outer) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetConeAngles(inner, outer); } void RLDS3D_GetSelConeOutsideVolume(LPLONG temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetConeOutsideVolume(temp); } void RLDS3D_GetSelMinimumDistance(D3DVALUE *temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetMinDistance(temp); } void RLDS3D_GetSelMaximumDistance(D3DVALUE *temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetMaxDistance(temp); } void RLDS3D_SetSelConeAngles(DWORD inner, DWORD outer) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetConeAngles(inner, outer, DS3D_IMMEDIATE); UpdateConeVisual(); } void RLDS3D_SetSelConeOutsideVolume(LONG temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetConeOutsideVolume(temp, DS3D_IMMEDIATE); } void RLDS3D_SetSelMinimumDistance(D3DVALUE temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetMinDistance(temp, DS3D_IMMEDIATE); } void RLDS3D_SetSelMaximumDistance(D3DVALUE temp) { if (!RLDS3D_SoundSelected) return; ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetMaxDistance(temp, DS3D_IMMEDIATE); } /* ** Orbit control windows have a pointer to the orbiting frame attached to them. The frame, in return, has the handle of the window in it's ** frameappdata. When the window is used (the value modified/Stop Orbit button hit) the window modifies the frame and itself appropriately. ** When the frame is modified (ie: Destroyed) it passes messages to the window appropriately. */ /* ** Structure to store info about an orbit */ struct ORBITDATA { LPDIRECT3DRMFRAME orbit_frame; LPDIRECT3DRMFRAME child_frame; D3DVECTOR axis; // Axis around which the orbit is going D3DVALUE speed; // Speed (angles per second) }; typedef ORBITDATA* LPORBITDATA; /* ** Structure to store info about a bullet, used by both the dialog box and the callback function */ struct BULLETDATA { LPDIRECT3DRMFRAME bullet_frame; D3DVECTOR vStartPosition; D3DVECTOR vDirection; D3DVALUE fSpeed; D3DVALUE fTime; }; typedef BULLETDATA* LPBULLETDATA; static void CDECL bulletCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delta) { LPBULLETDATA binfo = (LPBULLETDATA)arg; if (binfo->fTime <= D3DVAL(0.0)) { binfo->fTime = D3DVAL(10.0); // Ten seconds, 5 for before and 5 for after. D3DVALUE mult = D3DDivide(D3DMultiply(binfo->fTime, binfo->fSpeed), D3DVAL(-2.0)); // Calculate the displacement multiplier for calculating starting position obj->SetPosition(info->scene, binfo->vStartPosition.x + D3DMultiply(mult, binfo->vDirection.x), binfo->vStartPosition.y + D3DMultiply(mult, binfo->vDirection.y), binfo->vStartPosition.z + D3DMultiply(mult, binfo->vDirection.z)); } else binfo->fTime -= delta; } /* ** Stops the passed frame from orbiting or from bulleting */ void StopOrbiting(LPDIRECT3DRMFRAME stopme) { // If it isn't orbiting we don't need to stop it! if (!stopme) return; if (!stopme->GetAppData()) return; FRAMEAPPDATA* data = (FRAMEAPPDATA*)stopme->GetAppData(); if (data->bOrbiting) { if (data->hDlg) { // We explicitly call EndDialog on the orbit's control without sending it any message. As a result we have // to grab it's associated orbitdata and delete it ourselves. LPORBITDATA orbit_data = (LPORBITDATA)GetWindowLong(data->hDlg, DWL_USER); if (orbit_data) delete orbit_data; EndDialog(data->hDlg, TRUE); } // Get its parent LPDIRECT3DRMFRAME parent; stopme->GetParent(&parent); // Put this frame back into the scene frame (this assumes that's where it came from) info->scene->AddChild(stopme); // The parent frame of an orbiting visual is only around to make the object orbit, so we don't need it parent->Release(); data->bOrbiting = FALSE; data->hDlg = 0; } if (data->bBullet) { if (data->hDlg) { LPBULLETDATA bullet_data = (LPBULLETDATA)GetWindowLong(data->hDlg, DWL_USER); stopme->SetPosition(info->scene, bullet_data->vStartPosition.x, bullet_data->vStartPosition.y, bullet_data->vStartPosition.z); if (bullet_data) delete bullet_data; EndDialog(data->hDlg, TRUE); stopme->DeleteMoveCallback(bulletCallback, bullet_data); } stopme->SetVelocity(info->scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), TRUE); data->bBullet = FALSE; data->hDlg = 0; } } void RLDS3D_StopOrbitSelected() { StopOrbiting(sFrame); } /* ** Windows procedure for the orbit dialog box */ BOOL CALLBACK OrbitDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { lparam = lparam; LPORBITDATA my_data = (ORBITDATA*)GetWindowLong(win, DWL_USER); char lpszTS[100]; switch (msg) { case WM_INITDIALOG: // We've set it up to pass in the pointer to our case-specific data (through WM_INITDIALOG) // so we tuck it away for later use. This points to the ORBITDATA structure associated with // this dialog box SetWindowLong(win, DWL_USER, lparam); sprintf(lpszTS, "%f", ((LPORBITDATA)lparam)->speed); SendDlgItemMessage(win, IDC_ORBIT, WM_SETTEXT, 0, (LPARAM) lpszTS); SendDlgItemMessage(win, IDC_ORBIT, EM_SETLIMITTEXT, 100, 0); return TRUE; case WM_COMMAND: { if (wparam == IDOK) { // Stopping the orbit will end the dialog for us. StopOrbiting(my_data->child_frame); return TRUE; } else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_ORBIT) { // Get string length int stringlength = SendDlgItemMessage(win, IDC_ORBIT, EM_LINELENGTH, 0, 0); // Check to make sure the string isn't too long - atof accepts up to 100 chars if (stringlength > 99) return TRUE; lpszTS[0] = (char)stringlength; // Get string SendDlgItemMessage(win, IDC_ORBIT, EM_GETLINE, 0, (LPARAM) lpszTS); lpszTS[stringlength] = 0; // Store speed and set new rotation my_data->speed = D3DVAL(atof(lpszTS)); my_data->orbit_frame->SetRotation(info->scene, my_data->axis.x, my_data->axis.y, my_data->axis.z, my_data->speed); return TRUE; } else return FALSE; } break; } return FALSE; } /* ** Windows proc for the bullet dialog box */ BOOL CALLBACK BulletDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { lparam = lparam; LPBULLETDATA my_data = (BULLETDATA*)GetWindowLong(win, DWL_USER); char lpszTS[100]; switch (msg) { case WM_INITDIALOG: // We've set it up to pass in the pointer to our case-specific data (through WM_INITDIALOG) // so we tuck it away for later use. This points to the ORBITDATA structure associated with // this dialog box SetWindowLong(win, DWL_USER, lparam); sprintf(lpszTS, "%f", ((LPBULLETDATA)lparam)->fSpeed); SendDlgItemMessage(win, IDC_BULLET, WM_SETTEXT, 0, (LPARAM) lpszTS); SendDlgItemMessage(win, IDC_BULLET, EM_SETLIMITTEXT, 100, 0); return TRUE; case WM_COMMAND: { if (wparam == IDOK) { // Stopping the orbit will end the dialog for us. StopOrbiting(my_data->bullet_frame); return TRUE; } else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_BULLET) { // Get string length int stringlength = SendDlgItemMessage(win, IDC_BULLET, EM_LINELENGTH, 0, 0); // Check to make sure the string isn't too long - atof accepts up to 100 chars if (stringlength > 99) return TRUE; lpszTS[0] = (char)stringlength; // Get string SendDlgItemMessage(win, IDC_BULLET, EM_GETLINE, 0, (LPARAM) lpszTS); lpszTS[stringlength] = 0; // Store speed and set my_data->fSpeed = D3DVAL(atof(lpszTS)); my_data->bullet_frame->SetVelocity(info->scene, D3DMultiply(my_data->vDirection.x, my_data->fSpeed), D3DMultiply(my_data->vDirection.y, my_data->fSpeed), D3DMultiply(my_data->vDirection.z, my_data->fSpeed), TRUE); return TRUE; } else return FALSE; } break; } return FALSE; } /* ** Orbits the currently selected object around the camera... creates a frame which is centred at the camera and contains ** the selected frame, and then rotates that frame around the camera */ ULONG orbit_num = 1; void RLDS3D_OrbitSelected(void) { if (!sFrame) return; StopOrbiting(sFrame); FRAMEAPPDATA* tempdat; // Need to create orbit data, if not then can't orbit LPORBITDATA orbitdat = new ORBITDATA; if (!orbitdat) return; if (!sFrame->GetAppData()) { tempdat = new FRAMEAPPDATA(); if (!tempdat) return; sFrame->SetAppData((ULONG)(tempdat)); } else { tempdat = (LPFRAMEAPPDATA)sFrame->GetAppData(); } // Create a frame. Centre it around the camera. Add the selected frame. Rotate it! LPDIRECT3DRMFRAME orbiting_frame; if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &orbiting_frame))) return; orbiting_frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)); orbiting_frame->SetOrientation(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0)); orbiting_frame->AddChild(sFrame); D3DVECTOR vDir, vUp; info->camera->GetOrientation(info->scene, &vDir, &vUp); // Store orbit info orbitdat->orbit_frame = orbiting_frame; orbitdat->child_frame = sFrame; orbitdat->speed = D3DVAL(1.0); orbitdat->axis.x = vUp.x; orbitdat->axis.y = vUp.y; orbitdat->axis.z = vUp.z; orbiting_frame->SetRotation(info->scene, vUp.x, vUp.y, vUp.z, orbitdat->speed); // Set the boolean in the frame's appinfo to tell everyone it's orbiting tempdat->bOrbiting = TRUE; // Create a dialog box and orbitinfo so that this thing has something to control it HWND mydial = CreateDialogParam(hinst, "OrbitBox", hwndParent, (int(__stdcall*)(void))OrbitDlgProc, (LONG)orbitdat); if (mydial) { char name_string[50]; sprintf(name_string, "Orbit #%i", orbit_num++); SetWindowText(mydial, name_string); ShowWindow(mydial, SW_SHOW); UpdateWindow(mydial); tempdat->hDlg = mydial; } } /* ** Bullet the currently selected object. Bullets have two properties: Speed and bullet-time. The bullet passes through it's original ** position along the path defined by the viewer's original viewpoint (it heads towards the viewer). It takes 5 seconds to get to the ** viewer and five seconds once past the viewer, and continues doing thus until the bullet is stopped. Speed is controlled by the user. */ ULONG bullet_num = 1; void RLDS3D_BulletSelected(void) { if (!sFrame) return; StopOrbiting(sFrame); FRAMEAPPDATA* tempdat; if (!sFrame->GetAppData()) { tempdat = new FRAMEAPPDATA(); if (!tempdat) return; sFrame->SetAppData((ULONG)(tempdat)); } else { tempdat = (LPFRAMEAPPDATA)sFrame->GetAppData(); } // Create a new bullet object and attach our callback to the frame (it will release itself and put itself back // into position when it's done!) LPBULLETDATA bs = new BULLETDATA; if (!bs) return; sFrame->GetPosition(info->scene, &(bs->vStartPosition)); D3DVECTOR vDir, vUp; info->camera->GetOrientation(info->scene, &vDir, &vUp); // velocity heading towards camera bs->vDirection.x = -vDir.x; bs->vDirection.y = -vDir.y; bs->vDirection.z = -vDir.z; bs->bullet_frame = sFrame; bs->fTime = D3DVAL(10.0); // Ten seconds, 5 for before and 5 for after. bs->fSpeed = D3DVAL(10.0); D3DVALUE mult = D3DDivide(D3DMultiply(bs->fTime, bs->fSpeed), D3DVAL(-2.0)); // Calculate the displacement multiplier for calculating starting position sFrame->SetPosition(info->scene, bs->vStartPosition.x + D3DMultiply(mult, bs->vDirection.x), bs->vStartPosition.y + D3DMultiply(mult, bs->vDirection.y), bs->vStartPosition.z + D3DMultiply(mult, bs->vDirection.z)); sFrame->SetVelocity(info->scene, D3DMultiply(bs->vDirection.x, bs->fSpeed), D3DMultiply(bs->vDirection.y, bs->fSpeed), D3DMultiply(bs->vDirection.z, bs->fSpeed), TRUE); sFrame->AddMoveCallback(bulletCallback, (void*)bs); // Set the boolean in the frame's appinfo to tell everyone it's orbiting tempdat->bBullet = TRUE; // Create a dialog box and orbitinfo so that this thing has something to control it HWND mydial = CreateDialogParam(hinst, "BulletBox", hwndParent, (int(__stdcall*)(void))BulletDlgProc, (LONG)bs); if (mydial) { char name_string[50]; sprintf(name_string, "Bullet #%i", bullet_num++); SetWindowText(mydial, name_string); ShowWindow(mydial, SW_SHOW); UpdateWindow(mydial); tempdat->hDlg = mydial; } } /* ********************************** DIRECTSOUND 3D FUNCTIONS ***************************************** */ /* ** The RLDS3D interface allows you to attach sound(s) to the selected object which can then be played and ** whose 3D audio position will update according to the RL object's position ** ** Some functionality is applied to all sounds, and as a result, the RLDS3D interface keeps a record of ** existing sounds and which frames they are attached to. Removal of a frame and/or a sound updates these ** records */ /* ** CALLBACKS - update position as 3D objects move around */ // The listener callback is attached to the camera. This is done in CreateScene() static void CDECL listenerCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delta) { arg = arg; if (!lpDS) return; D3DVECTOR rlvCameraInfo, rlvCameraUp; info->camera->GetPosition(info->scene, &rlvCameraInfo); lp3DListenerInfo->SetPosition(rlvCameraInfo.x, rlvCameraInfo.y, rlvCameraInfo.z, DS3D_DEFERRED); info->camera->GetOrientation(info->scene, &rlvCameraInfo, &rlvCameraUp); lp3DListenerInfo->SetOrientation(rlvCameraInfo.x, rlvCameraInfo.y, rlvCameraInfo.z, rlvCameraUp.x, rlvCameraUp.y, rlvCameraUp.z, DS3D_DEFERRED); info->camera->GetVelocity(info->scene, &rlvCameraInfo, TRUE); lp3DListenerInfo->SetVelocity(rlvCameraInfo.x, rlvCameraInfo.y, rlvCameraInfo.z, DS3D_DEFERRED); lp3DListenerInfo->CommitDeferredSettings(); } // Sounds are updated whenever their frame moves using Render Callbacks. This is the callback function. static void CDECL soundCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delay) { arg = arg; LPDIRECT3DRMFRAME tFrame = (LPDIRECT3DRMFRAME)obj; if (!lpDS) return; // Get the sound from the frame's app data LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)tFrame->GetAppData(); if (!lpAppDat) return; // Get the 3D sound buffer and remove the sound callback if it's NULL since it shouldn't exist if (!lpAppDat->Sound || !lpAppDat->i3DSound) { releaseSoundCallback(tFrame); return; } D3DVECTOR rlvVisualInfo, rlvVisualUp; tFrame->GetPosition(info->scene, &rlvVisualInfo); lpAppDat->i3DSound->SetPosition(rlvVisualInfo.x, rlvVisualInfo.y, rlvVisualInfo.z, DS3D_DEFERRED); tFrame->GetOrientation(info->scene, &rlvVisualInfo, &rlvVisualUp); lpAppDat->i3DSound->SetConeOrientation(rlvVisualInfo.x, rlvVisualInfo.y, rlvVisualInfo.z, DS3D_DEFERRED); tFrame->GetVelocity(info->scene, &rlvVisualInfo, TRUE); lpAppDat->i3DSound->SetVelocity(rlvVisualInfo.x, rlvVisualInfo.y, rlvVisualInfo.z, DS3D_DEFERRED); lp3DListenerInfo->CommitDeferredSettings(); } // This adds a sound callback function to a frame void setSoundCallback(LPDIRECT3DRMFRAME frame) { frame->AddMoveCallback(soundCallback, NULL); } // Removes a sound callback from a frame void releaseSoundCallback(LPDIRECT3DRMFRAME frame) { frame->DeleteMoveCallback(soundCallback, NULL); } /* ** Sound records - keeps track of which frames have sounds associated with them and ** adds/removes the sounds as required. */ struct SoundRecord { LPDIRECT3DRMFRAME lpFrame; SoundRecord* next; }; SoundRecord* top = NULL; BOOL Recurse_Remove(LPDIRECT3DRMFRAME owner, SoundRecord* record) { if (!record) return FALSE; if (Recurse_Remove(owner, record->next)) { SoundRecord* temp = record->next; record->next = temp->next; delete temp; } if (owner == record->lpFrame) return TRUE; return FALSE; } // Removes a frame from the records void RemoveSoundRecord(LPDIRECT3DRMFRAME owner) { if (!owner) return; // Don't bother deleting frames without appdata and a sound associated with them // because they SHOULDN'T be on the list. if (!owner->GetAppData()) return; LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(owner->GetAppData()); if (!data->Sound) return; // Release the sound and remove the soundrecord from the list. data->i3DSound->Release(); data->i3DSound = NULL; data->Sound->Release(); data->Sound = NULL; releaseSoundCallback(owner); if (Recurse_Remove(owner, top)) { SoundRecord* temp = top; top = top->next; delete temp; } } // Adds a frame to the records void AddSoundRecord(LPDIRECT3DRMFRAME owner, char* sound_filename) { if (!lpDS) return; if (!owner) return; if (!sound_filename) return; // Removes all previous sounds from this frame RemoveSoundRecord(owner); // Create frame data for this frame if it hasn't already been done LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(owner->GetAppData()); if (!data) { data = new FRAMEAPPDATA(); if (!data) return; owner->SetAppData((ULONG)data); } // Create the sound and attach it to the frame data->Sound = DSLoad3DSoundBuffer(lpDS, sound_filename); if (!data->Sound) return; // Query to get the 3D interface, destroy the sound buffer if it's not available... data->Sound->QueryInterface(IID_IDirectSound3DBuffer, (void**)&data->i3DSound); if (!data->i3DSound) { data->Sound->Release(); data->Sound = NULL; return; } // Set the minimum distance at which the sound's amplitude should decay. data->i3DSound->SetMinDistance((D3DVALUE)10.0, DS3D_IMMEDIATE); setSoundCallback(owner); SoundRecord* temp = new SoundRecord; temp->next = top; top = temp; top->lpFrame = owner; } /* ** Functionality for users */ // Stops all the sounds in the world. (Actually, only in the local RL world.) // Runs through the records of all the sounds and stops them. void RLDS3D_StopAllSounds() { if (!lpDS) return; SoundRecord* temp = top; while (temp) { LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(temp->lpFrame->GetAppData()); if (data) { if (data->Sound) data->Sound->Stop(); } temp = temp->next; } } // Removes all sounds in the world void RLDS3D_RemoveAllSounds() { if (!lpDS) return; while (top) RemoveSoundRecord(top->lpFrame); UpdateConeVisual(); } // Plays the sound associated with the currently selected object void RLDS3D_PlaySound(BOOL bIsLooping) { if (!sFrame) return; if (!lpDS) return; LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)sFrame->GetAppData(); if (lpAppDat) { if (lpAppDat->Sound) { lpAppDat->Sound->Stop(); lpAppDat->Sound->SetCurrentPosition(0); if (bIsLooping) { lpAppDat->Sound->Play(0,0,DSBPLAY_LOOPING); } else { lpAppDat->Sound->Play(0,0,0); } } } } // Stops the sound associated with the currently selected object void RLDS3D_StopSelectedSound() { if (!sFrame) return; if (!lpDS) return; LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)sFrame->GetAppData(); if (lpAppDat) { if (lpAppDat->Sound) { lpAppDat->Sound->Stop(); } } } // Removes the sound from the currently selected object void RLDS3D_RemoveSound() { RLDS3D_StopSelectedSound(); if (!sFrame) return; if (!lpDS) return; RemoveSoundRecord(sFrame); UpdateConeVisual(); } // Attaches a sound (filename provided) to the selected frame void RLDS3D_AttachSound(char* filename) { // Removes the sound attached to the currently selected item (if it exists) RLDS3D_RemoveSound(); if (!sFrame) return; if (!lpDS) return; AddSoundRecord(sFrame, filename); UpdateConeVisual(); } /* ************************************* MISC. MAINTENANCE FUNCTIONS ************************************** */ /* ** Allows external users access to the RL Device to deal with Windows-related issues ** (See case WM_ACTIVATE: and case WM_PAINT: in viewer source for examples of HandleActivate() and HandlePaint()) ** Design note: This was done to save time from the conversion from the old version of the viewer rather than ** having RLDS3D_HandleActivate(), etc. */ LPDIRECT3DRMDEVICE RLDS3D_WinDevice() { if (!info) return NULL; return info->dev; } /* ** Handles activation messages from the window */ void RLDS3D_HandleActivate(WPARAM wparam) { if (!info || !info->dev) return; LPDIRECT3DRMWINDEVICE windev; if (D3DRM_SUCCEED(info->dev->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &windev))) { windev->HandleActivate(wparam); windev->Release(); } } /* ** Handles paint messages from the window - the paintstruct which BeginPaint has been called on is ** passed to it */ void RLDS3D_HandlePaint(PAINTSTRUCT* ps) { if (!info) return; LPDIRECT3DRMWINDEVICE windev; if (D3DRM_SUCCEED(info->dev->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &windev))) { windev->HandlePaint(ps->hdc); windev->Release(); } } /* ** Tells whether or not sound is initialized */ BOOL RLDS3D_SoundInitialized() { if (lpDS) return TRUE; return FALSE; } /* ** Tells whether or not a frame is selected */ BOOL RLDS3D_FrameSelected() { if (sFrame) return TRUE; return FALSE; } /* ** Render the scene into the viewport. */ void RLDS3D_Render(D3DVALUE time_delta) { // When the WM_SIZE message passes 0's as size to the ResizeViewport we know that it's minimized, in which case we don't render it. if (info->bMinimized == TRUE) return; D3DRM_SUCCEED(info->scene->Move(time_delta)); D3DRM_SUCCEED(info->view->Clear()); D3DRM_SUCCEED(info->view->Render(info->scene)); D3DRM_SUCCEED(info->dev->Update()); } /* * Resize the viewport and device when the window size changes. */ void RLDS3D_ResizeViewport(int width, int height) { int view_width = info->view->GetWidth(); int view_height = info->view->GetHeight(); int dev_width = info->dev->GetWidth(); int dev_height = info->dev->GetHeight(); if (!(width && height)) { info->bMinimized = TRUE; return; } else info->bMinimized = FALSE; if (view_width == width && view_height == height) return; else info->bMinimized = FALSE; if (width <= dev_width && height <= dev_height) { info->view->Release(); D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, width, height, &info->view)); info->view->SetBack(D3DVAL(400.0)); } int ret; if (!RebuildDevice(hwndParent, info, width, height)) { ret = MessageBox(hwndParent, "Unable to create Direct3D device", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK); PostMessage(hwndParent, WM_CLOSE,0,0); }; } /* ** Sets/Gets the polygon fill mode */ D3DRMFILLMODE RLDS3D_GetPolygonFillMode(void) { return (D3DRMFILLMODE)(info->dev->GetQuality() & D3DRMFILL_MASK); } void RLDS3D_SetPolygonFillMode(D3DRMFILLMODE quality) { D3DRMRENDERQUALITY oldq = info->dev->GetQuality(); oldq = (oldq & ~D3DRMFILL_MASK) | quality; info->dev->SetQuality(oldq); } /* ** Sets/Gets the polygon shading mode */ D3DRMSHADEMODE RLDS3D_GetPolygonShadeMode(void) { return (D3DRMSHADEMODE)(info->dev->GetQuality() & D3DRMSHADE_MASK); } void RLDS3D_SetPolygonShadeMode(D3DRMSHADEMODE quality) { D3DRMRENDERQUALITY oldq = info->dev->GetQuality(); oldq = (oldq & ~D3DRMSHADE_MASK) | quality; info->dev->SetQuality(oldq); } /* ** Sets/Gets the color model for the viewport (RGB or mono (256-color-based)) */ D3DRMCOLORMODEL RLDS3D_GetColourModel(void) { return info->model; } void RLDS3D_SetColourModel(D3DRMCOLORMODEL model) { info->model = model; int ret; if (!RebuildDevice(hwndParent, info, info->dev->GetWidth(), info->dev->GetHeight())) { ret = MessageBox(hwndParent, "Unable to selected Direct3D device", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK); PostMessage(hwndParent, WM_CLOSE,0,0); } } /* ** Sets/Gets whether or not lighting is on */ BOOL RLDS3D_GetLighting(void) { D3DRMLIGHTMODE mode = (D3DRMLIGHTMODE)(info->dev->GetQuality() & D3DRMLIGHT_MASK); if (mode == D3DRMLIGHT_ON) return TRUE; return FALSE; } void RLDS3D_SetLighting(BOOL new_val) { D3DRMRENDERQUALITY qual = info->dev->GetQuality() & ~D3DRMLIGHT_MASK; if (new_val) qual |= D3DRMLIGHT_ON; else qual |= D3DRMLIGHT_OFF; info->dev->SetQuality(qual); } /* ** Sets/Gets whether or not dithering is on */ BOOL RLDS3D_GetDither(void) { return info->dev->GetDither(); } void RLDS3D_SetDither(BOOL dither) { info->dev->SetDither(dither); } /* ** Sets/Gets texture quality (only relevant for RGB modes) */ D3DRMTEXTUREQUALITY RLDS3D_GetTextureQuality(void) { return info->dev->GetTextureQuality(); } void RLDS3D_SetTextureQuality(D3DRMTEXTUREQUALITY new_quality) { info->dev->SetTextureQuality(new_quality); } /* ************************************* INTERNAL FUNCTIONS (Not part of API) ******************************** */ /* ** Given a bounding box this generates a visual representation of it using rods and cones */ static LPDIRECT3DRMMESHBUILDER makeBox(D3DRMBOX* box) { LPDIRECT3DRMMESHBUILDER mesh; static D3DVECTOR zero = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)}; static D3DVECTOR dir = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)}; D3DVECTOR a, b; if (!D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&mesh))) return NULL; dir.z = box->max.z + D3DVAL(1.0); AddRod(mesh, D3DVAL(0.05), zero, dir); a = dir; a.z += D3DVAL(0.6); AddCone(mesh, D3DVAL(0.2), dir, a); a = box->min; b = a; b.y = box->max.y; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.x = box->max.x; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.y = box->min.y; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.x = box->min.x; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.z = box->max.z; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.x = box->max.x; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.y = box->max.y; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.x = box->min.x; AddRod(mesh, D3DVAL(0.05), a, b); a = b; b.y = box->min.y; AddRod(mesh, D3DVAL(0.05), a, b); b.y = box->max.y; a = b; b.z = box->min.z; AddRod(mesh, D3DVAL(0.05), a, b); a = b = box->max; b.z = box->min.z; AddRod(mesh, D3DVAL(0.05), a, b); a.y = box->min.y; b = a; b.z = box->min.z; AddRod(mesh, D3DVAL(0.05), a, b); if (!D3DRM_SUCCEED(mesh->SetColor(D3DRMCreateColorRGB(D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0))))) { mesh->Release(); return NULL; } return mesh; } /* ** Given a box, this creates a mesh in the shape of a 16-sided speaker cone aimed forward with requested angle */ #define CONE_POINTS 16 #define pi 3.14159 static LPDIRECT3DRMMESHBUILDER makeSpeaker(D3DRMBOX* box, D3DVALUE in_angle) { if (!box) return NULL; D3DVALUE angle = in_angle / D3DVAL(2.0); DWORD* speaker_faces = new DWORD[CONE_POINTS*4+1]; if (!speaker_faces) return NULL; memset(speaker_faces, 0, sizeof(DWORD[CONE_POINTS*4+1])); LPDIRECT3DRMMESHBUILDER mesh; if (!D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&mesh))) { delete speaker_faces; return NULL; } static D3DVECTOR zero = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)}; D3DVECTOR v[CONE_POINTS+1]; // center of the cone v[CONE_POINTS] = zero; int looper; // Angle along XZ plane which is rotated CONE_POINT times to form cone D3DVECTOR base_angle; base_angle.z = (box->max.z + D3DVAL(2.0)) * (D3DVALUE)cos(angle * pi / 180.0); base_angle.x = (box->max.z + D3DVAL(2.0)) * (D3DVALUE)sin(angle * pi / 180.0); base_angle.y = D3DVAL(0.0); for (looper=0; looper<CONE_POINTS; looper++) { v[looper].z = base_angle.z; v[looper].x = base_angle.x * (D3DVALUE)cos((looper*2*pi)/CONE_POINTS); v[looper].y = base_angle.x * (D3DVALUE)sin((looper*2*pi)/CONE_POINTS); speaker_faces[looper*4] = 3; speaker_faces[looper*4+1] = looper % CONE_POINTS; speaker_faces[looper*4+2] = (looper + 1) % CONE_POINTS; speaker_faces[looper*4+3] = CONE_POINTS; } v[CONE_POINTS] = zero; if (!D3DRM_SUCCEED(mesh->AddFaces(CONE_POINTS+1, v, 0, NULL, speaker_faces, NULL))) { delete speaker_faces; mesh->Release(); return NULL; } for (looper=0; looper<CONE_POINTS; looper++) { speaker_faces[looper*4+2] = looper % CONE_POINTS; speaker_faces[looper*4+1] = (looper + 1) % CONE_POINTS; } if (!D3DRM_SUCCEED(mesh->AddFaces(CONE_POINTS+1, v, 0, NULL, speaker_faces, NULL))) { delete speaker_faces; mesh->Release(); return NULL; } delete speaker_faces; if (!D3DRM_SUCCEED(mesh->SetColor(D3DRMCreateColorRGB(D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0))))) { mesh->Release(); return NULL; } if (!D3DRM_SUCCEED(mesh->SetQuality((mesh->GetQuality() & ~D3DRMSHADE_MASK) | D3DRMSHADE_FLAT))) { mesh->Release(); return NULL; } return mesh; } /* ** Selects the given visual inside the given frame */ void UpdateConeVisual(void) { if (!sFrame) return; if (!sFrame->GetAppData()) return; LPFRAMEAPPDATA fd = (LPFRAMEAPPDATA)sFrame->GetAppData(); if (!fd->i3DSound) return; if (showBoxes && sVisual) { D3DRMBOX box; LPDIRECT3DRMMESHBUILDER builder; sFrame->DeleteVisual(selectionSpeaker); sVisual->GetBox(&box); DWORD temp, outer; fd->i3DSound->GetConeAngles(&temp, &outer); builder = makeSpeaker(&box, D3DVAL(temp)); builder->CreateMesh(&selectionSpeaker); sFrame->AddVisual(selectionSpeaker); selectionSpeaker->Release(); builder->Release(); } } void SelectVisual(LPDIRECT3DRMMESHBUILDER visual, LPDIRECT3DRMFRAME frame) { RLDS3D_DeselectVisual(); sVisual = visual; sFrame = frame; if (sVisual) { LPDIRECT3DRMLIGHTARRAY lights; sLight = 0; sFrame->GetLights(&lights); if (lights) { if (lights->GetSize()) { lights->GetElement(0, &sLight); sLight->Release(); /* reinstate reference count */ } lights->Release(); } if (showBoxes && visual) { D3DRMBOX box; LPDIRECT3DRMMESHBUILDER builder; sVisual->GetBox(&box); builder = makeBox(&box); builder->CreateMesh(&selectionBox); sFrame->AddVisual(selectionBox); selectionBox->Release(); builder->Release(); UpdateConeVisual(); } } } LPGUID FindDevice(D3DCOLORMODEL cm) { LPDIRECTDRAW lpDD; LPDIRECT3D lpD3D; D3DFINDDEVICESEARCH search; static D3DFINDDEVICERESULT result; HRESULT error; if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK) return NULL; if (lpDD->QueryInterface(IID_IDirect3D, (void**) &lpD3D) != DD_OK) { lpDD->Release(); return NULL; } memset(&search, 0, sizeof search); search.dwSize = sizeof search; search.dwFlags = D3DFDS_COLORMODEL; search.dcmColorModel = (cm == D3DCOLOR_MONO) ? D3DCOLOR_MONO : D3DCOLOR_RGB; memset(&result, 0, sizeof result); result.dwSize = sizeof result; error = lpD3D->FindDevice(&search, &result); lpD3D->Release(); lpDD->Release(); if (error != D3D_OK) return NULL; else return &result.guid; } /* * Create the device and viewport. */ static BOOL CreateDevice(HWND win, AppInfo* info) { RECT r; int bpp; HDC hdc; GetClientRect(win, &r); if (!D3DRM_SUCCEED(lpD3DRM->CreateDeviceFromClipper(lpDDClipper, NULL, r.right, r.bottom, &info->dev))) return FALSE; hdc = GetDC(win); bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(win, hdc); switch (bpp) { case 1: info->dev->SetShades(4); lpD3DRM->SetDefaultTextureShades(4); break; case 16: info->dev->SetShades(32); lpD3DRM->SetDefaultTextureColors(64); lpD3DRM->SetDefaultTextureShades(32); info->dev->SetDither(FALSE); break; case 24: info->dev->SetShades(256); lpD3DRM->SetDefaultTextureColors(64); lpD3DRM->SetDefaultTextureShades(256); info->dev->SetDither(FALSE); break; default: info->dev->SetDither(FALSE); } if (!CreateScene(info)) { info->dev->Release(); return FALSE; } if (!D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, info->dev->GetWidth(), info->dev->GetHeight(), &info->view))) { info->dev->Release(); return FALSE; } info->view->SetBack(D3DVAL(5000.0)); return TRUE; } /* * Creates a simple scene and adds it to the main scene */ static BOOL CreateScene(AppInfo* info) { LPDIRECT3DRMFRAME light; LPDIRECT3DRMLIGHT light1, light2; // Note that if something fails, we don't bother freeing up everything we've created... the caller to CreateScene should destroy // the lpD3DRM object and that should happily release everything created with it. // Also note that, since we're just the viewer, if there's a critical error we pass a quit message with our error message since // we'd want to quit if the initialize failed anyways... if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(NULL, &info->scene))) return FALSE; if (!D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light1))) return FALSE; if (!D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1), D3DVAL(0.1), D3DVAL(0.1), &light2))) return FALSE; if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &light))) return FALSE; light->SetPosition(info->scene, D3DVAL(2.0), D3DVAL(2.0), D3DVAL(5.0)); light->SetOrientation(info->scene, D3DVAL(-1.0), D3DVAL(-1.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0)); light->AddLight(light1); info->scene->AddLight(light2); if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &info->camera))) return FALSE; info->camera->SetPosition(info->scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)); // Add a callback to the camera's frame so that the listener is updated with the camera info->camera->AddMoveCallback(listenerCallback, NULL); light->Release(), light1->Release(), light2->Release(); return TRUE; } /* * Regenerate the device if the color model changes or the window size * changes. */ static BOOL RebuildDevice(HWND win, AppInfo* info, int width, int height) { int old_dither = info->dev->GetDither(); D3DRMRENDERQUALITY old_quality = info->dev->GetQuality(); int old_shades = info->dev->GetShades(); info->view->Release(); info->dev->Release(); LPGUID guid = FindDevice(info->model); if (!guid) return FALSE; if (!D3DRM_SUCCEED(lpD3DRM->CreateDeviceFromClipper(lpDDClipper, guid, width, height, &info->dev))) return FALSE; info->dev->SetDither(old_dither); info->dev->SetQuality(old_quality); info->dev->SetShades(old_shades); width = info->dev->GetWidth(); height = info->dev->GetHeight(); if (!D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, width, height, &info->view))) return FALSE; info->view->SetBack(D3DVAL(400.0)); return TRUE; } /* * Place an object in front of the camera. */ static void PlaceMesh(LPDIRECT3DRMMESHBUILDER mesh, AppInfo *info) { LPDIRECT3DRMFRAME frame; if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) return; frame->AddVisual(mesh); frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(15.0)); frame->Release(); } HRESULT loadTextures(char *name, void *arg, LPDIRECT3DRMTEXTURE *tex) { char* ext = LSTRRCHR(name, (int)'.'); if (ext && !lstrcmpi(ext, ".ppm")) if (D3DRM_SUCCEED(lpD3DRM->LoadTexture(name, tex))) return 0; return -1; } /* ** Finds the last occurance of bChar in a null-terminated string, good for finding a pointer to the extension of a filename */ char* LSTRRCHR( const char* lpString, int bChar ) { if( lpString != NULL ) { const char* lpBegin; lpBegin = lpString; while( *lpString != 0 ) { lpString++; } while( 1 ) { if( *lpString == bChar ) { return (char*)lpString; } if( lpString == lpBegin ) { break; } lpString--; } } return NULL; } /* LSTRRCHR */ /* ** Strange little function to pick a color from a table using standardized Windows stuff */ int ChooseNewColor(HWND win, D3DCOLOR* current) { CHOOSECOLOR cc; COLORREF clr; COLORREF aclrCust[16]; int i; for (i = 0; i < 16; i++) aclrCust[i] = RGB(255, 255, 255); clr = RGB ( (int) (255 * D3DRMColorGetRed(*current)), (int) (255 * D3DRMColorGetGreen(*current)), (int) (255 * D3DRMColorGetBlue(*current)) ); memset(&cc, 0, sizeof(CHOOSECOLOR)); cc.lStructSize = sizeof(CHOOSECOLOR); cc.hwndOwner = win; cc.rgbResult = clr; cc.lpCustColors = aclrCust; cc.Flags = CC_RGBINIT|CC_FULLOPEN; if (ChooseColor(&cc)) { *current = D3DRMCreateColorRGB ( D3DVAL(GetRValue(cc.rgbResult) / D3DVAL(255.0)), D3DVAL(GetGValue(cc.rgbResult) / D3DVAL(255.0)), D3DVAL(GetBValue(cc.rgbResult) / D3DVAL(255.0)) ); return TRUE; } else return FALSE; }